#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#include "head.h"
#include "gyro.h"

char cmd[20] = {};
int uid = 0;

// 开始运行
void *start_run(void *arg);
// 注册
int user_Register(int *clifd, MYSQL *p_sql);
// 登录
int user_Login(int *clifd, MYSQL *p_sql);
// 用户注销
int delete_user(int *clifd, MYSQL *p_sql, int uid);
// 用户改密
int change_user(int *clifd, MYSQL *p_sql, int uid);
// 客户端上传
void c_up(int *clifd, MYSQL *p_sql, int uid);
// 客户端下载
void c_down(int *clifd, MYSQL *p_sql, int uid);
// 返回文件列表
void c_list(int *clifd);

// 主函数
int main()
{
	printf("[%s] 服务器创建中...\n", get_time(2));
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (0 > sockfd)
	{
		perror("socket");
		return -1;
	}

	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;

	addr.sin_port = htons(60000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t len = sizeof(addr);

	printf("[%s] 服务器创建成功...\n", get_time(2));
	if (bind(sockfd, (struct sockaddr *)&addr, len))
	{
		perror("bind");
		return -1;
	}

	printf("[%s] 设置监听...\n", get_time(2));
	if (listen(sockfd, 5))
	{
		perror("listen");
		return -1;
	}

	printf("[%s] 等待客户端连接...\n", get_time(2));
	for (;;)
	{
		struct sockaddr_in addrcli = {};
		int *clifd = (int *)malloc(sizeof(int));
		*clifd = accept(sockfd, (struct sockaddr *)&addrcli, &len);
		if (0 > *clifd)
		{
			perror("accept");
			continue;
		}

		pthread_t pid;

		pthread_create(&pid, NULL, start_run, (void *)clifd);
	}

	return 0;
}

// 开始运行
void *start_run(void *arg)
{
	int *clifd = (int *)arg;
	char up[20] = "上传文件";
	char down[20] = "下载文件";
	char see[20] = "查看目录";
	char quit[20] = "客户端退出";
	char Register[20] = "客户端注册";
	char Login[20] = "客户端登录";
	char change[20] = "客户端改密";
	char delete[20] = "客户端注销";

	int c_size = 0;

	MYSQL *p_sql = mysql_init(NULL);
	if (p_sql == NULL)
	{
		printf("[%s] mysql初始化失败\n", get_time(2));
		return (void *)NULL;
	}

	/*数据库链接*/
	if (mysql_real_connect(p_sql, "localhost", "root", "admin", "file_test", 0, NULL, 0) == NULL)
	{
		printf("[%s] mysql链接失败\n", get_time(2));
		mysql_close(p_sql);
		return (void *)NULL;
	}
	else
	{
		printf("[%s] mysql链接成功\n", get_time(2));
	}

	while (1)
	{
		c_size = read(*clifd, cmd, sizeof(cmd));
		if (strcmp(Register, cmd) == 0)
		{
			printf("[%s] 收到客户端的注册指令\n", get_time(2));
			user_Register(clifd, p_sql);
		}
		else if (strcmp(Login, cmd) == 0)
		{
			printf("[%s] 收到客户端的登录指令\n", get_time(2));
			if (uid = user_Login(clifd, p_sql))
			{
				break;
			}
		}
		else if (strcmp(quit, cmd) == 0)
		{
			printf("[%s] 收到服务端的退出指令\n", get_time(2));
			pthread_exit(0);
			mysql_close(p_sql);
			return (void *)NULL;
		}
	}

	for (;;)
	{
		c_size = read(*clifd, cmd, sizeof(cmd));
		if (-1 == c_size)
		{
			printf("[%s] read函数出错！\n", get_time(2));
		}

		if (strcmp(up, cmd) == 0)
		{
			printf("[%s] 收到客户端的上传指令\n", get_time(2));
			c_up(clifd, p_sql, uid);
			memset(cmd, 0, 20);
		}
		else if (strcmp(down, cmd) == 0)
		{
			printf("[%s] 收到客户端的下载指令\n", get_time(2));
			c_down(clifd, p_sql, uid);
			memset(cmd, 0, 20);
		}
		else if (strcmp(delete, cmd) == 0)
		{
			printf("[%s] 收到客户端%d的注销指令\n", get_time(2), uid);
			delete_user(clifd, p_sql, uid);
			memset(cmd, 0, 20);
		}
		else if (strcmp(change, cmd) == 0)
		{
			printf("[%s] 收到客户端%d的改密指令\n", get_time(2), uid);
			change_user(clifd, p_sql, uid);
		}
		else if (strcmp(see, cmd) == 0)
		{
			printf("[%s] 收到客户端的目录指令\n", get_time(2));
			c_list(clifd);
			memset(cmd, 0, 20);
		}
		else if (strcmp(quit, cmd) == 0)
		{
			printf("[%s] 收到客户端的退出指令\n", get_time(2));
			pthread_exit(0);
			mysql_close(p_sql);
			return (void *)NULL;
		}
	}
	// char *str = "我死了";
	// pthread_exit(str);
}

/*******************************************************************************************************/

// 注册
int user_Register(int *clifd, MYSQL *p_sql)
{
	mysql_set_character_set(p_sql, "utf8");
	User user;
	char sql[90];
	char buf[1024];
	char r_size = 0;
	int *id = (int *)malloc(20);
	read(*clifd, id, sizeof(int));
	read(*clifd, user.name, sizeof(user.name));
	read(*clifd, user.passwd, sizeof(user.passwd));

	sprintf(sql, "insert into user(id,name,passwd) values(%d,\'%s\',\'%s\')", *id, user.name, user.passwd);

	query_sql(p_sql, sql);
	printf("[%s] 客户端%d注册成功\n", get_time(2), *id);
	free(id);


	return 0;
}

// 登录
int user_Login(int *clifd, MYSQL *p_sql)
{

	int flag = 0;

	int w_size = 0;
	char buf[1024] = {};
	int r_size = 0;
	char sql[90];
	User user;
	int *id = (int *)malloc(20);
	read(*clifd, id, sizeof(int));
	user.id = *id;
	read(*clifd, user.passwd, sizeof(user.passwd));

	sprintf(sql, "select id,name from user where id=%d and passwd='%s'", *id, user.passwd);

	mysql_query(p_sql, sql);

	MYSQL_RES *result = mysql_store_result(p_sql);

	int rows = mysql_num_rows(result);
	if (rows == 0)
	{
		w_size = write(*clifd, "fail", 5);
		free(id);
		mysql_free_result(result);
		return 0;
	}

	w_size = write(*clifd, "success", 8);
	printf("[%s] 客户端%d登录成功\n", get_time(2), *id);
	free(id);

	return user.id;
}

// 用户注销
int delete_user(int *clifd, MYSQL *p_sql, int uid)
{
	int w_size = 0;
	char buf[1024] = {};
	int r_size = 0;
	char sql[90];

	sprintf(sql, "delete from user where id=%d", uid);
	mysql_query(p_sql, sql);

	printf("[%s] 客户端%d注销成功\n", get_time(2), uid);
	write(*clifd, "success", 8);
	return 1;
}

// 用户改密
int change_user(int *clifd, MYSQL *p_sql, int uid)
{
	int w_size = 0;
	char buf[1024] = {};
	int r_size = 0;
	char sql[90];
	char new_passwd[50], new_passwd_1[50];

	read(*clifd, new_passwd, sizeof(new_passwd));
	read(*clifd, new_passwd_1, sizeof(new_passwd_1));
	if (strcmp(new_passwd, new_passwd_1) == 0)
	{
		sprintf(sql, "update user set passwd='%s' where id=%d", new_passwd, uid);
		mysql_query(p_sql, sql);
		printf("[%s] 客户端%d改密成功\n", get_time(2), uid);
		write(*clifd, "success", 8);
	}
	else
	{
		write(*clifd, "fail", 5);
	}

	return 1;
}

/*******************************************************************************************************/

// 上传
void c_up(int *clifd, MYSQL *p_sql, int uid)
{
	int flag = 0;
	int r_size = 0;
	int w_size = 0;
	char buf[1024] = {};

	w_size = write(*clifd, "success", 8);
	read(*clifd, buf, 10);
	if (strncmp(buf, "error", 10) == 0)
	{
		printf("[%s] 收到客户端返回error,接收终止\n", get_time(2));
		return;
	}
	else if (strncmp(buf, "success", 10) == 0)
	{
		printf("[%s] 收到客户端返回success,继续接收\n", get_time(2));
	}
	else
	{
		printf("[%s] 收到客户端异常数据:%s,接收终止\n", get_time(2), buf);
		return;
	}

	// 用于存储文件名，长度50，不够则需要自行加长
	char filename[50] = {};
	memset(filename, 0, sizeof(filename));
	// 发送success给客户端，告知客户端可以传输文件名
	w_size = write(*clifd, "success", 8);
	int f_size = read(*clifd, filename, sizeof(filename));
	if (-1 == f_size)
	{
		printf("[%s] read函数出错！\n", get_time(2));
	}
	printf("[%s] 收到文件名:%s\n", get_time(2), filename);



	// 发送success给客户端，告知可以开始文件传输
	w_size = write(*clifd, "success", 8);

	int fd = open(filename, O_CREAT | O_RDWR, 0777);

	do
	{
		memset(buf, 0, sizeof(buf));
		r_size = read(*clifd, buf, sizeof(buf));
		// printf("[收到字节数:%d ", r_size);

		w_size = write(fd, buf, r_size);
		// printf("写入文件字节数:%d ", w_size);


		w_size = write(*clifd, "success", 8);
		// printf("发送success给客户端 ]\n");

		flag++;
	} while (r_size == 1024);

	sleep(1);

	if (flag > 0)
	{
		char result[20] = "success";
		char file[50] = {0};
		printf("[%s] 文件传输完毕\n", get_time(2));
		mysql_set_character_set(p_sql, "utf8");
		sprintf(file, "insert into file_date(uid,filename,date_time,action) values(%d,\'%s\',\'%s\','上传')", uid, filename, get_time(2));
		query_sql(p_sql, file);
		write(*clifd, result, strlen(result) + 1);
	}
	else
	{
		char result[20] = "error";
		printf("[%s] 文件传输失败\n\n", get_time(2));
		write(*clifd, result, strlen(result) + 1);
	}
	close(fd);
	return;
}

// 下载
void c_down(int *clifd, MYSQL *p_sql, int uid)
{
	DIR *dir;
	dir = opendir(".");
	char list[1024] = {};
	struct dirent *dirent;
	int r_size = 0;
	int w_size = 0;
	char buf[1024] = {};
	char buf2[20] = {};
	char filename[50] = {};
	char filename2[51] = {};

	w_size = write(*clifd, "success", 8);
	r_size = read(*clifd, buf, sizeof(buf));

	printf("[%s] 发送当前下载目录列表给客户端\n", get_time(2));

	// 获取目录下所有文件名
	while ((dirent = readdir(dir)) != NULL)
	{
		strcat(list, dirent->d_name);
		strcat(list, " ");
	}

	int l_size = write(*clifd, list, strlen(list) + 1);
	if (-1 == l_size)
	{
		printf("[%s] read函数出错！\n", get_time(2));
	}

	printf("[%s] 等待接收文件名...\n", get_time(2));
	int f_size = read(*clifd, filename, sizeof(filename));
	if (-1 == f_size)
	{
		printf("[%s] read函数出错！\n", get_time(2));
	}
	strncpy(filename2, filename, 50);
	strcat(filename2, " ");
	if (strstr(list, filename2) == NULL || strncmp(filename2, " ", 1) == 0 || strncmp(filename2, "  ", 2) == 0)
	{
		char result[6] = "error";
		printf("[%s] 文件:%s 不存在,下载终止\n", get_time(2), filename);

		write(*clifd, result, strlen(result));
		return;
	}
	else
	{
		char result[8] = "success";
		write(*clifd, result, strlen(result));

		memset(buf, 0, sizeof(buf));
		snprintf(buf, 150, "ls -ll %s | awk '{print $5}'", filename);
		FILE *temp_fp = NULL;
		temp_fp = popen(buf, "r");
		if (temp_fp == NULL)
		{
			printf("[%s] 获取文件大小失败\n", get_time(2));
		}
		memset(buf, 0, sizeof(buf));
		fscanf(temp_fp, "%s", buf);
		printf("[%s] 文件大小:%sB\n", get_time(2), buf);
		pclose(temp_fp);
		memset(buf, 0, sizeof(buf));

		int fd = open(filename, O_RDONLY);

		// 设置文件读写位置为文件尾部
		lseek(fd, 0, SEEK_END);
		// 获取文件字节数（尾部位置）
		off_t end_pos = lseek(fd, 0, SEEK_CUR);
		// printf("end_pos:%d\n", end_pos);
		// 设置文件读写位置为文件头部
		lseek(fd, 0, SEEK_SET);


		do
		{
			memset(buf, 0, sizeof(buf));
			r_size = read(fd, buf, sizeof(buf));
			w_size = write(*clifd, buf, r_size);
			read(*clifd, result, sizeof(result));
			if (strncmp(result, "success", 10) == 0)
			{
				// printf("成功收到客户端端返回的success]\n");
			}

			off_t cur_pos = lseek(fd, 0, SEEK_CUR);
			// printf("cur_pos:%d\n", cur_pos);
			if (cur_pos == end_pos && w_size == 1024)
			{
				char end[1] = "\0";
				// printf("[读取文件字节数:1 ");
				w_size = write(*clifd, end, sizeof(end));
				// printf("发送字节数:%d ", w_size);
				read(*clifd, buf2, sizeof(buf2));

				if (strncmp(buf2, "success", 10) == 0)
				{
					// printf("成功收到客户端端返回的success]\n");
				}
				else
				{
					printf("收到客户端返回的异常数据:%s]\n", buf2);
				}
				break;
			}
		} while (r_size == 1024);
		char file[50] = {0};
		printf("[%s] 文件:%s 发送完毕\n", get_time(2), filename);
		mysql_set_character_set(p_sql, "utf8");
		sprintf(file, "insert into file_date(uid,filename,date_time,action) values(%d,\'%s\',\'%s\','下载')", uid, filename, get_time(2));
		query_sql(p_sql, file);
		close(fd);
	}

	return;
}

// 文件列表
void c_list(int *clifd)
{
	DIR *dir;
	dir = opendir(".");
	char list[1024] = {};
	struct dirent *dirent;
	while ((dirent = readdir(dir)) != NULL)
	{
		strcat(list, dirent->d_name);
		strcat(list, " ");
	}

	int l_size = write(*clifd, list, strlen(list) + 1);
	if (-1 == l_size)
	{
		printf("[%s] write函数出错！\n", get_time(2));
	}

	memset(list, 0, 1024);

	char dirname[20] = {};
	int d_size = read(*clifd, dirname, sizeof(dirname));
	if (-1 == d_size)
	{
		printf("[%s] read函数出错！\n", get_time(2));
	}
	printf("[%s] 收到客户端的数据:%s\n", get_time(2), dirname);

	if (strncmp(dirname, "...", 20) == 0)
	{
		closedir(dir);
		return;
	}

	if (strncmp(dirname, ".", 20) == 0)
	{
		dir = opendir(".");
		while ((dirent = readdir(dir)) != NULL)
		{
			strcat(list, dirent->d_name);
			strcat(list, " ");
		}
		l_size = write(*clifd, list, strlen(list) + 1);
	}
	else if (strncmp(dirname, "..", 20) == 0)
	{
		chdir("..");
		dir = opendir(".");
		while ((dirent = readdir(dir)) != NULL)
		{
			strcat(list, dirent->d_name);
			strcat(list, " ");
		}
		l_size = write(*clifd, list, strlen(list) + 1);
	}
	else
	{
		int re = chdir(dirname);
		if (re == -1)
		{
			char result[20] = "目录名错误";
			int err = write(*clifd, result, strlen(result) + 1);
			if (-1 == err)
			{
				printf("[%s] write函数出错！\n", get_time(2));
			}
		}
		else
		{
			dir = opendir(".");
			while ((dirent = readdir(dir)) != NULL)
			{
				strcat(list, dirent->d_name);
				strcat(list, " ");
			}
			l_size = write(*clifd, list, strlen(list) + 1);
		}
	}
	closedir(dir);
	return;
}
